session反序列化
session反序列化
PHP session基础
什么是session?
Session
一般称为“会话控制“,简单来说就是是一种客户与网站/服务器更为安全的对话方式。一旦开启了 session
会话,便可以在网站的任何页面使用或保持这个会话,从而让访问者与网站之间建立了一种“对话”机制。
PHP session
可以看做是一个特殊的变量,且该变量是用于存储关于用户会话的信息,或者更改用户会话的设置,需要注意的是,PHP Session
变量存储单一用户的信息,并且对于应用程序中的所有页面都是可用的,且其对应的具体 session
值会存储于服务器端,这也是与 cookie
的主要区别,所以seesion
的安全性相对较高。
php session的工作流程
(1) 开始会话时,php
会尝试从请求中查找会话id
。若发现请求信息中不存在session id
,php
就会自动调用php_session_create_id
函数创建一个新的会话,并且在响应头中使用set-cookie
头部发送给客户端保存;
(2) php
会将会话中的数据设置到$_SESSION
变量中;
(3) 当会话停止时,会自动读取$_SESSION
中的内容,并将其进行序列化,发送到保存管理器进行保存。
具体工作流程图如下:
session在php.ini中的配置
session.gc_divisor
php session垃圾回收机制相关配置
session.sid_bits_per_character
指定编码的会话ID字符中的位数
session.save_path=””
该配置主要设置
session
的存储路径session.save_handler=””
该配置主要设定用户自定义存储函数,如果想使用PHP内置
session
存储机制之外的可以使用这个函数session.use_strict_mode
严格会话模式,严格会话模式不接受未初始化的会话ID并重新生成会话ID
session.use_cookies
指定是否在客户端用 cookie 来存放会话 ID,默认启用
session.cookie_secure
指定是否仅通过安全连接发送
cookie
,默认关闭session.use_only_cookies
指定是否在客户端仅仅使用
cookie
来存放会话 ID,启用的话,可以防止有关通过 URL 传递会话 ID 的攻击session.name
指定会话名以用做
cookie
的名字,只能由字母数字组成,默认为PHPSESSID
session.auto_start
指定会话模块是否在请求开始时启动一个会话,默认值为 0,不启动
session.cookie_lifetime
指定了发送到浏览器的 cookie 的生命周期,单位为秒,值为 0 表示“直到关闭浏览器”。默认为 0
session.cookie_path
指定要设置会话
cookie
的路径,默认为 /session.cookie_domain
指定要设置会话
cookie
的域名,默认为无,表示根据cookie
规范产生cookie
的主机名session.cookie_httponly
将Cookie标记为只能通过HTTP协议访问,即无法通过脚本语言(例如JavaScript)访问Cookie,此设置可以有效地帮助通过XSS攻击减少身份盗用
session.serialize_handler
定义用来序列化/反序列化的处理器名字,默认使用
php
,还有其他引擎,且不同引擎的对应的session的存储方式不相同,具体可见下文所述session.gc_probability
该配置项与
session.gc_divisor
合起来用来管理garbage collection
,即垃圾回收进程启动的概率session.gc_divisor
该配置项与
session.gc_probability
合起来定义了在每个会话初始化时启动垃圾回收进程的概率session.gc_maxlifetime
指定过了多少秒之后数据就会被视为“垃圾”并被清除,垃圾搜集可能会在
session
启动的时候开始( 取决于session.gc_probability
和session.gc_divisor
)session.referer_check
包含有用来检查每个
HTTP Referer
的子串。如果客户端发送了Referer
信息但是在其中并未找到该子串,则嵌入的会话 ID 会被标记为无效。默认为空字符串session.cache_limiter
指定会话页面所使用的缓冲控制方法(
none/nocache/private/private_no_expire/public
)。默认为nocache
session.cache_expire
以分钟数指定缓冲的会话页面的存活期,此设定对
nocache
缓冲控制方法无效。默认为 180session.use_trans_sid
指定是否启用透明 SID 支持。默认禁用
session.sid_length
配置会话ID字符串的长度。 会话ID的长度可以在22到256之间。默认值为32。
session.trans_sid_tags
指定启用透明sid支持时重写哪些HTML标签以包括会话ID
session.trans_sid_hosts
指定启用透明sid支持时重写的主机,以包括会话ID
session.sid_bits_per_character
配置编码的会话ID字符中的位数
session.upload_progress.enabled
启用上传进度跟踪,并填充
$ _SESSION
变量, 默认启用。session.upload_progress.cleanup
读取所有POST数据(即完成上传)后,立即清理进度信息,默认启用
session.upload_progress.prefix
配置
$ _SESSION
中用于上传进度键的前缀,默认为upload_progress_
session.upload_progress.name
$ _SESSION
中用于存储进度信息的键的名称,默认为PHP_SESSION_UPLOAD_PROGRESS
session.upload_progress.freq
定义应该多长时间更新一次上传进度信息
session.upload_progress.min_freq
更新之间的最小延迟
session.lazy_write
配置会话数据在更改时是否被重写,默认启用
session存储机制
(1) 由session.serialize_handler
来定义引擎;
(2) 默认以文件的方式存储;
(3) 文件命名方式以sess_sessionid;
(4) 文件内容是一序列化的方式进行存储;
php的三种引擎
session.serialize_handler
定义了三种引擎:
处理器名称 | 存储格式 |
---|---|
php | 键名 + 竖线 + 经过serialize() 函数序列化处理的值 |
php_binary | 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize() 函数序列化处理的值 |
php_serialize | 经过serialize() 函数序列化处理的数组 |
注:自 PHP 5.5.4 起可以使用php_serialize
php处理器
代码实例
1 |
|
请求后保存的session文件内容的结果为:
1 | session|s:9:"greetdawn"; |
php_binary处理器
代码实例
1 |
|
请求后保存的session文件内容的结果为:
1 | #passwdpasswdpasswdpasswdpasswdpassws:9:"greetdawn"; |
php_serialize处理器
代码实例
1 |
|
请求后保存的session文件内容的结果为:
1 | a:1:{s:6:"passwd";s:9:"greetdawn";} |
php session反序化漏洞
漏洞概念
PHP session反序列化漏洞,简单点说,就是当网站序列化并存储Session与反序列化并读取Session的方式不同时就可能导致session反序列化漏洞的产生。
漏洞demo(session_demo)
index.php
1 |
|
demo.php
1 |
|
漏洞分析
我们发现index.php
通过php_serialize
引擎处理session
数据,demo.php
通过php
引擎处理session
数据;
通过前面的知识我们知道php_serialize
引擎以正常的反序列化格式存储和加载session数据,php以|的方式存储和加载session数据的;
分析demo的源码,对lemon类进行实例化,得到其序列化的值为:O:5:"lemon":1:{s:2:"hi";s:10:"phpinfo();";}
请求index.php带入序列化的值,http://192.168.1.191:32769/?name=|O:5:"lemon":1:{s:2:"hi";s:10:"phpinfo();";}
最终sess文件的保存数据格式如下:
a:1:{s:4:"name";s:44:"|O:5:"lemon":1:{s:2:"hi";s:10:"phpinfo();";}";}
当我们再次请求demo.php文件时,此文件会议php处理器的方式加载session文件,此时``a:1:{s:4:”name”;s:44:”|O:5:”为 键名,
a:1:{s:4:”name”;s:44:”|O:5:”`为值
那么最后的值就会被正常的进行反序列化,调用eval函数;
例题实战
session_upload
(1) 题目源码
index.php
1 |
|
class.php
1 |
|
phpinfo.php
1 |
|
(2) 题目分析
请求phpinfo.php获取session的配置信息如下:
默认是采用php_serialize
处理器处理session
,session.upload_progress.cleanup
配置为Off,session.upload_progress.enabled
配置为On;
session.upload_progress.enabled
当它为开启状态时,PHP能够在每一个文件上传时监测上传进度。当一个上传在处理中,同时POST一个与php.ini中设置的session.upload_progress.name
同名变量时,上传进度就可以在$_SESSION
中获得。当PHP检测到这种POST请求时,它会在$_SESSION
中添加一组数据, 索引是session.upload_progress.prefix
与 session.upload_progress.name
连接在一起的值。
分析前面的代码并没有发现可以向服务器发送数据的点,根据前面所讲的特性,我们可以通过上传文件的方式向目标session写入数据。
并且我们发现i.php与phpinfo.php使用的session解析器不相同;
(3) 漏洞利用
分析class.php的源码构造poc如下:
1 |
|
本地构造上表单文件index.html
1 | <form action="http://192.168.1.191:32771/index.php" method="POST" enctype="multipart/form-data"> |
bp截包,修改PHP_SESSION_UPLOAD_PROGRESS的值为class序列化的值
|O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";r:2;s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:69:"include('php://filter/read=convert.base64-encode/resource=flag.php');";}}}
session_jarvis
(1) 题目源码
index.php
1 |
|
phpinfo.php
1 |
|
(2) 题目分析
请求链接发现首页源码,根据源码传入phpinfo任意参数,执行phpinfo(),得到配置信息如下:
根据以上信息发现是典型的利用了上传这个点传入session数据的;
并且通过扫描发现存在phpinfo.php这个页面,请求发现这个页面使用的是默认的php_serialize解析器
根据两页面使用的解析器不同特征,可以达成任意的反序列化利用
(3) 漏洞利用
构造上传表单
1 | <form action="http://192.168.1.191:32768/index.php" method="POST" enctype="multipart/form-data"> |
构造目录文件扫描序列化代码
1 |
|
抓取上传包,修改filename带入序列化的值
构造读取flag序列化代码
1 |
|
获得flag